package com.heima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.model.Utils.threadlocal.AppThreadLocalUtils;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dto.UserSearchDto;
import com.heima.model.search.vo.SearchArticleVo;
import com.heima.model.user.pojo.ApUser;
import com.heima.search.service.ApUserSearchService;
import com.heima.search.service.ArticleSearchService;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Created by ZYJ on 2021/6/8 19:24
 */
@Service
@Slf4j
public class ArticleSearchServiceImpl implements ArticleSearchService {
    public static final String ARTICLE_INDEX_NAME = "app_info_article";
    @Autowired
    RestHighLevelClient restHighLevelClient;
    @Autowired
    ApUserSearchService apUserSearchService;
    /**
     * es文章搜索
     * @param userSearchDto
     * @return
     */
    @Override
    public ResponseResult search(UserSearchDto userSearchDto) {

        //1 参数检查
        if(userSearchDto.getSearchWords() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        int pageSize = userSearchDto.getPageSize();
        if(pageSize == 0){
            pageSize = 10;
        }
        if (userSearchDto.getFromIndex() == 0){
            ApUser user = AppThreadLocalUtils.getUser();
            userSearchDto.setEntryId(user == null ? null : user.getId());
            apUserSearchService.insert(userSearchDto);
        }
        //构建搜索请求
        SearchRequest searchRequest = new SearchRequest(ARTICLE_INDEX_NAME);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); //构建所有请求参数
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //关键词查询
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title",userSearchDto.getSearchWords());
        boolQueryBuilder.must(matchQueryBuilder);
        //范围查询
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("publishTime").lt(userSearchDto.getMinBehotTime());
        boolQueryBuilder.filter(rangeQueryBuilder);
        //按照发布时间倒序
        sourceBuilder.sort("publishTime", SortOrder.DESC);
        //查询0-pageSize条数据
        sourceBuilder.from(0).size(userSearchDto.getPageSize());
        //高亮条件
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.field("title");
        highlightBuilder.postTags("</span>");
        //高亮条件 查询条件 创建完成 放到请求体中
        sourceBuilder.highlighter(highlightBuilder);
        sourceBuilder.query(boolQueryBuilder);
        searchRequest.source(sourceBuilder);

        List<SearchArticleVo> articleVoList = new ArrayList<>();
        //调用客户端执行搜索请求 得到响应对象
        try {

            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //得到命中结果
            SearchHit[] searchHits = searchResponse.getHits().getHits();

            for (SearchHit doc : searchHits) {
                //得到文章文档的json字符串
                String articleJson = doc.getSourceAsString();
                SearchArticleVo articleVo = JSON.parseObject(articleJson, SearchArticleVo.class);
                //得到高亮结果 然后替换vo里的title变成高亮处理过的
                Map<String, HighlightField> highlightFields = doc.getHighlightFields();
                HighlightField highlightField = highlightFields.get("title");
                //得到高亮结果后 需要把里面的text片段拼接起来
                String highlightTitle = StringUtils.join(highlightField.getFragments());
                articleVo.setTitle(highlightTitle);
                articleVoList.add(articleVo);

            }

        } catch (IOException e) {
            e.printStackTrace();
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }

        return ResponseResult.okResult(articleVoList);
    }
    /**
     * 添加索引文章
     * @param article
     */
    @Override
    public void saveArticle(SearchArticleVo article) {
        IndexRequest indexRequest = new IndexRequest(ARTICLE_INDEX_NAME);
        indexRequest.id(article.getId().toString()).source(JSON.toJSONString(article), XContentType.JSON);
        try {
            restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("索引库添加文章数据失败: {}",article);
        }
    }

    /**
     * 删除索引文章
     * @param articleId
     */
    @Override
    public void deleteArticle(Long articleId) {
        DeleteRequest deleteRequest = new DeleteRequest(ARTICLE_INDEX_NAME);
        deleteRequest.id(articleId.toString());

        try {
            restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("索引库添加文章数据失败: {}",articleId);
        }
    }
}
